﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.ObjectModel;

namespace System.Windows.Forms;

/// <summary>
///  Represents a collection of <see cref="TaskDialogButton"/> objects.
/// </summary>
public class TaskDialogButtonCollection : Collection<TaskDialogButton>
{
    // HashSet to detect duplicate items.
    private readonly HashSet<TaskDialogButton> _itemSet = [];

    /// <summary>
    ///  Initializes a new instance of the <see cref="TaskDialogButtonCollection"/> class.
    /// </summary>
    public TaskDialogButtonCollection()
    {
    }

    internal TaskDialogPage? BoundPage { get; set; }

    /// <summary>
    ///  Creates and adds a <see cref="TaskDialogButton"/> to the collection.
    /// </summary>
    /// <param name="text">The text of the custom button.</param>
    /// <param name="enabled">A value indicating whether the button can respond to user interaction.</param>
    /// <param name="allowCloseDialog">A value that indicates whether the task dialog should close
    ///  when this button is clicked.
    /// </param>
    /// <returns>The created <see cref="TaskDialogButton"/>.</returns>
    /// <exception cref="InvalidOperationException">
    ///  This collection is currently bound to a task dialog.
    /// </exception>
    public TaskDialogButton Add(string? text, bool enabled = true, bool allowCloseDialog = true)
    {
        TaskDialogButton button = new(text, enabled, allowCloseDialog);
        Add(button);

        return button;
    }

    /// <inheritdoc/>
    /// <exception cref="ArgumentNullException">
    ///  <paramref name="item"/> is <see langword="null"/>.
    /// </exception>
    /// <exception cref="ArgumentException">
    ///  <paramref name="item"/> has already been added to the collection.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    ///  <paramref name="item"/> is already a part of a different collection.
    ///  - or -
    ///  This collection is currently bound to a task dialog.
    /// </exception>
    protected override void SetItem(int index, TaskDialogButton item)
    {
        ArgumentNullException.ThrowIfNull(item);

        // Disallow collection modification, so that we don't need to copy it
        // when binding the TaskDialogPage.
        BoundPage?.DenyIfBound();
        DenyIfHasOtherCollection(item);

        TaskDialogButton oldItem = this[index];
        if (oldItem != item)
        {
            // First, add the new item (which will throw if it is a duplicate entry),
            // then remove the old one.
            if (!_itemSet.Add(item))
            {
                throw new ArgumentException(SR.TaskDialogControlAlreadyAddedToCollection);
            }

            _itemSet.Remove(oldItem);

            oldItem.Collection = null;
            item.Collection = this;
        }

        base.SetItem(index, item);
    }

    /// <inheritdoc/>
    /// <exception cref="ArgumentNullException">
    ///  <paramref name="item"/> is <see langword="null"/>.
    /// </exception>
    /// <exception cref="ArgumentException">
    ///  <paramref name="item"/> has already been added to the collection.
    /// </exception>
    /// <exception cref="InvalidOperationException">
    ///  <paramref name="item"/> is already a part of a different collection.
    ///  - or -
    ///  This collection is currently bound to a task dialog.
    /// </exception>
    protected override void InsertItem(int index, TaskDialogButton item)
    {
        ArgumentNullException.ThrowIfNull(item);

        // Disallow collection modification, so that we don't need to copy it
        // when binding the TaskDialogPage.
        BoundPage?.DenyIfBound();
        DenyIfHasOtherCollection(item);

        if (!_itemSet.Add(item))
        {
            throw new ArgumentException(SR.TaskDialogControlAlreadyAddedToCollection);
        }

        item.Collection = this;
        base.InsertItem(index, item);
    }

    /// <inheritdoc/>
    /// <exception cref="InvalidOperationException">
    ///  This collection is currently bound to a task dialog.
    /// </exception>
    protected override void RemoveItem(int index)
    {
        // Disallow collection modification, so that we don't need to copy it
        // when binding the TaskDialogPage.
        BoundPage?.DenyIfBound();

        TaskDialogButton oldItem = this[index];
        oldItem.Collection = null;
        _itemSet.Remove(oldItem);
        base.RemoveItem(index);
    }

    /// <inheritdoc/>
    /// <exception cref="InvalidOperationException">
    ///  This collection is currently bound to a task dialog.
    /// </exception>
    protected override void ClearItems()
    {
        // Disallow collection modification, so that we don't need to copy it
        // when binding the TaskDialogPage.
        BoundPage?.DenyIfBound();

        foreach (TaskDialogButton button in this)
        {
            button.Collection = null;
        }

        _itemSet.Clear();
        base.ClearItems();
    }

    private void DenyIfHasOtherCollection(TaskDialogButton item)
    {
        if (item.Collection is not null && item.Collection != this)
            throw new InvalidOperationException(SR.TaskDialogControlIsPartOfOtherCollection);
    }
}
